
#include "enet.hpp"
#include "Internal.Transceiver.Core.h"

unsigned int InternalTransceiver::NetworkThreadFunc()
{
	enet_initialize();

	ENetAddress address;
	ENetEvent event;
	ENetHost *host;
	ENetPeer *peer;
	bool is_server;

	srand(enet_time_get());

	address.host = ENET_HOST_ANY;
	address.port = 30000;

	unsigned int internalID = 0;
	while(internalID < 1000)
		internalID = (unsigned int)rand();

	host = enet_host_create (&address, 32, ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT, 0, 0);
	if (host != NULL)
	{
		fprintf(stderr, "Server listening on localhost:30000\n");
		is_server = true;
	}
	else
	{
		host = enet_host_create(NULL, 1, ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT, 57600 / 8, 14400 / 8);
		if (host == NULL)
		{
			fprintf(stderr, "Fatal error\n");
			return 0;
		}
		else
		{
			enet_address_set_host (&address, "localhost");
			address.port = 30000;

			peer = enet_host_connect(host, &address, ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT, internalID);    
			if (peer == NULL)
			{
				fprintf(stderr, "Fatal error\n");
				return 0;
			}
			if (enet_host_service(host, &event, 3000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT)
			{
				//fprintf(stderr, "Connection to localhost:30000 succeeded\n");
				//fprintf(stderr, "CONNECT %d [localpeer]\n", internalID);
				is_server = false;
			}
			else
			{
				enet_peer_reset (peer);
			}
		}
	}

	assert(host);
	if(!is_server)
		assert(peer->state == ENET_PEER_STATE_CONNECTED);

	while(IsRunning())
	{
		ENetEvent event;
		if (enet_host_service(host, &event, 1000) > 0)
		{
			switch (event.type)
			{
			case ENET_EVENT_TYPE_CONNECT:
				if(is_server)
				{
					fprintf(stderr, "CONNECT %d %x:%u\n", event.peer->eventData, event.peer->address.host, event.peer->address.port);
					event.peer->data = (void *)event.peer->eventData;

					// send new guy to others
					{
						SmartPtr<NetworkStream> str = new NetworkStream();
						str->WriteLong(ENET_EVENT_TYPE_CONNECT);
						str->WriteLong(1);
						str->WriteLong((unsigned int)event.peer->data);

						ENetPacket *packet = enet_packet_create(str->GetStreamData(), str->GetStreamDataLength(), ENET_PACKET_FLAG_RELIABLE);
						enet_host_broadcast_except(host, event.peer, 0, packet);
					}

					// send list to new guy
					{						
						ENetPeer * currentPeer;
						SmartPtr<NetworkStream> str = new NetworkStream();

						unsigned int peerCount = 0;
						for (currentPeer = host->peers; currentPeer < & host->peers [host->peerCount]; ++ currentPeer)
						   if (currentPeer->state == ENET_PEER_STATE_CONNECTED)
							 peerCount++;

						str->WriteLong(ENET_EVENT_TYPE_CONNECT);
						str->WriteLong(peerCount);

						for (currentPeer = host->peers; currentPeer < & host->peers [host->peerCount]; ++ currentPeer)
							if (currentPeer->state == ENET_PEER_STATE_CONNECTED)
								str->WriteLong((unsigned int)currentPeer->data);

						ENetPacket *packet = enet_packet_create(str->GetStreamData(), str->GetStreamDataLength(), ENET_PACKET_FLAG_RELIABLE);
						enet_peer_send(event.peer, 0, packet);
					}
				}
				break;

			case ENET_EVENT_TYPE_RECEIVE:
				//fprintf(stderr, "A packet of length %u containing %s was received from %s on channel %u.\n",
				//		event.packet -> dataLength,
				//		event.packet -> data,
				//		event.peer -> data,
				//		event.channelID);
				{
					SmartPtr<NetworkStream> str = new NetworkStream();
					str->WriteRaw(event.packet -> data, event.packet -> dataLength);
					str->Seek(0);

					if(!is_server && event.channelID == 0)
					{
						unsigned int code = str->ReadLong();
						switch(code)
						{
						case ENET_EVENT_TYPE_CONNECT:
							{
								unsigned int count = str->ReadLong();
								while(count > 0)
								{
									fprintf(stderr, "CONNECT %d\n", str->ReadLong());
									count--;
								}
							}
							break;
						case ENET_EVENT_TYPE_DISCONNECT:
							{
								unsigned int count = str->ReadLong();
								while(count > 0)
								{
									fprintf(stderr, "DISCONNECT %d\n", str->ReadLong());
									count--;
								}
							}
							break;
						default:
							break;
						}
					}

					enet_packet_destroy (event.packet);
				}
				break;
		       
			case ENET_EVENT_TYPE_DISCONNECT:
				if(!is_server)
				{
					_running = false;
					break;
				}

				// send new guy info to others
				{
					SmartPtr<NetworkStream> str = new NetworkStream();
					str->WriteLong(ENET_EVENT_TYPE_DISCONNECT);
					str->WriteLong(1);
					str->WriteLong((unsigned int)event.peer->data);

					ENetPacket *packet = enet_packet_create(str->GetStreamData(), str->GetStreamDataLength(), ENET_PACKET_FLAG_RELIABLE);
					enet_host_broadcast(host, 0, packet);
				}

				fprintf(stderr, "DISCONNECT %d %x:%u\n", (unsigned int)event.peer->data, event.peer->address.host, event.peer->address.port);
				event.peer -> data = NULL;	
				break;
			}
		}
	}

	if(!is_server)
	{
		assert(peer);
		enet_peer_disconnect(peer, 0);
		enet_host_service(host, &event, 1000);
		enet_peer_reset (peer);
		
		fprintf(stderr, "DISCONNECT %d\n", internalID);
	}

	enet_host_destroy(host);
	enet_deinitialize();

	return 0;
}
